#!/usr/bin/python
"""
Back end for Tama.
"""

import random
import os
import time

import inflect
import Levenshtein

from . import gibber
from . import vtils
from . import config

__author__  = "dogfeet@pm.me"

p = inflect.engine()

## CLASSES

class Tama():
    """
    A tama is a single mysterious creature. This class manages its stats and
    behavior.

    Attributes:
        true_name:    the name a tama has on creation
        false_name:   player-assigned name
        name_level:   name rank for levenshtein distance between true and false
                      names
        found:        the timestamp for when it is created
        egg:          descriptor for egg type
        locale:       original place where egg was found
        temperament:  current temperament
        temperament_log: archive of temperaments, in order from recent->first
        status:       current phase of life
        phases:       timestamps for when phase was reached (0 if not reached)
        check_in_age: number of check-ins needed to advance out of that status
        check_ins:    number of times player has performed a check-in action
        last_checked: timestamp for last check-in action
    """

    ## data management

    def __init__ (self):
        """
        Create an empty Tama object.
        """

        self.true_name = ""
        self.false_name = ""
        self.name_level = 5
        self.found = 0
        self.egg = ""
        self.locale = ""
        self.temperament = ""
        self.temperament_log = []
        self.status = ""
        self.phases = {}
        self.check_in_age = {}
        self.check_ins = 0
        self.last_checked = 0

    def __str__(self):
        """
        Return a string representation of self.
        """

        return "A tama called {name}, currently in {status} form.".format(
                name=self.true_name.capitalize(), status=self.status)

    def to_dict(self):
        """
        Converts all data to a dictionary.
        """

        tama_data = {
                "true name": self.true_name,
                "false name": self.false_name,
                "name level": self.name_level,
                "found": self.found,
                "egg": self.egg,
                "locale": self.locale,
                "temperament": self.temperament,
                "temperament log": self.temperament_log,
                "status": self.status,
                "check in age": self.check_in_age,
                "phases": self.phases,
                "check ins": self.check_ins,
                "last checked": self.last_checked
                }

        return tama_data

    def save(self, tamadir=os.path.join(os.path.expanduser('~'), ".tama", "tamas")):
        """
        Save to disk! Tamadir refers to directory to write into.

        todo: currently, this is unfortunately hardcoded, and none of the save
        function calls actually provide their own tamadir.
        """

        filename = os.path.join(tamadir, self.true_name + ".tama")
        vtils.write_dict_as_json(filename, self.to_dict())

    def load(self, tamadir, tama_name):
        """ 
        Loads a tama from file based on tama_name, in tamadir, then returns own
        name for verification. Blows away whatever data is currently held.

        todo: throw exception if not found?
        """

        filename = os.path.join(tamadir, tama_name + ".tama")

        tama_data = vtils.open_json_as_dict(filename)

        self.true_name = tama_data.get("true name", "abomination")
        self.false_name = tama_data.get("false name", "fool")
        self.name_level = tama_data.get("name level", 5)
        self.found = tama_data.get("found", 0)
        self.egg = tama_data.get("egg", "incorporeal")
        self.locale = tama_data.get("locale", "hell")
        self.temperament = tama_data.get("temperament", "calamitous")
        self.temperament_log = tama_data.get("temperament log", [])
        self.status = tama_data.get("status", vtils.rainbow("eldritch"))
        self.check_in_age = tama_data.get("check in age", config.age_table)
        self.phases = tama_data.get("phases", {"egg": -100})
        self.check_ins = tama_data.get("check ins", -1)
        self.last_checked = tama_data.get("last checked", -1)

        return self.true_name

    def destroy(self):
        """
        Irreversible; destroys data file. Tama data may still be lingering in
        memory somewhere, though.
        """

        os.remove(os.path.join(config.TAMA_DIR, self.true_name+".tama"))

        return

    def self_report(self):
        """
        Outputs a player-friendly status report, which does not reveal any
        forbidden knowledge. Information shown depends on tama age and
        relationship with player.

        todo: better text wrapping
        """

        if self.status == "egg":
            description = "{} egg".format(self.egg)
            found = "it"
            current = ""
        else:
            if self.status in ["adult", "elder"]:
                description = "{} {}".format(self.temperament, self.status)
            else:
                # for larva, teenager, and carcass
                description = self.status

            # for larva and up
            found = "its {} egg".format(self.egg)
            current = "It's been a {} for {}.".format(self.status,
                    vtils.pretty_time(self.since_phase()))

        if self.false_name:
            name_info = """
This {status} has been designated "{name}", which need not be a permanent name.""".format(status=self.status,
                    name=self.false_name.capitalize())
        else:
            name_info = ""

        report = """
You gaze upon the {description} under your care, remembering when you found
{found} in the {terrain} {time} ago. {current}
{name_info}\
""".format(
        description=description,
        found=found,
        terrain=self.locale,
        time=vtils.pretty_time(self.since_found()),
        name_info=name_info,
        status=self.status,
        current=current
        )

        return report

    def check_in(self):
        """
        Perform check-in actions; increment check-in counter.
        """

        self.check_ins += 1
        self.last_checked = time.time()
        self.save()

    ## modifying (all of these should save at completion)

    def spawn(self):
        """
        Do all the dice-rolls and data logging to spawn a new tama. Return
        true name for confirmation.
        """

        self.true_name = gibber.new_word(random.randint(2,3))
        self.false_name = ""
        self.name_level = 5
        self.found = time.time()
        self.egg = random.choice([
            "swollen",
            "charming",
            "lonely",
            "optimistic",
            "demanding"])
        self.locale = random.choice([
            "grass",
            "hills",
            "river",
            "trees",
            "dirt"])
        self.temperament = "unsettled"
        self.temperament_log = []
        self.status = "egg"
        self.phases = {
                "egg": time.time(),
                "larva": 0,
                "teenager": 0,
                "adult": 0,
                "elder": 0,
                "carcass": 0
                }
        last = 0
        for x in config.age_table.keys():
            roll = random.randrange(int(.8*config.age_table.get(x)), config.age_table.get(x))
            count = last + roll
            self.check_in_age.update({x: count})
            last = count
        self.check_ins = 0
        self.last_checked = 0

        self.save()

        return self.true_name

    def age_up(self):
        """
        Advance to the next stage.
        """

        message = ""

        # do nothing if dead
        if self.status == "carcass":
            return message 

        # otherwise, advance
        next_phase = config.tama_phase.get(self.status) + 1
        self.status = list(config.tama_phase)[next_phase]

        self.phases.update({self.status: time.time()})
        self.save()

        if self.status == "larva":
            message = """\
A seam begins to open at the top of the {} egg. After a few pounding heartbeats,
a larval form slowly pushes its way out.

Before the egg breaks away, you catch the soft larva. It lets out a yawn, then
looks at you with adoring eyes.""".format(self.egg)
        elif self.status == "teenager":
            message = """\
The larva begins to quiver, an unnerving motion that causes you to briefly
avert your eyes, even as you feel concern.

When you look back, it stands before you as an awkward, long-limbed teenager,
seeming a little antsy."""
        elif self.status == "adult":
            message = """\
The teenager stretches lazily, then begins to glow.

As the glow fades away, you find that the stretching has permanently affected
its body, as it is now a full grown adult. """
        elif self.status == "elder":
            message = """\
The adult lets out a small sigh, then sits carefully on the ground as a soft
light radiates from its head.

When the light fades, the bent form of an elder greets you warmly."""
        elif self.status == "carcass":
            message = """\
A bright flash of light blinds you momentarily, piercing your mind with a
high-pitched tone, which briefly floods your heart with trepidation.

As you recover, you discover that only a carcass remainds in the {} before
you.""".format(self.locale)

        return message

    ## free actions (does not count as a check-in)

    def set_temperament(self, new_temperament):
        """
        Set a new temperament, archiving the current one.
        """

        self.temperament_log.append(self.temperament)
        self.temperament = new_temperament
        self.save()

    ## check-in actions

    def squeeze(self):
        """
        Generates the appropriate feedback message for current aging status;
        check-in counter increases afterwads.

        Returns: string
        """

        squeeze = ""

        # todo: this can definitely better, probably outsource all this text

        feedback = config.squeeze_feedback.get(self.status).get(int(self.age_percentage()/25))

        #print(self.age_percentage())

        if self.status == "egg":
            squeeze = "You clumsily squeeze the egg. It feels {} in your hands.".format(feedback)
        elif self.status == "larva":
            squeeze = "You gently squeeze the larva. It emits {}.".format(feedback)
        elif self.status == "teenager":
            squeeze = "You squeeze the teenager with encouragement. It {}.".format(feedback)
        elif self.status == "adult":
            squeeze = "You give the adult an affectionate squeeze. It {}.".format(feedback)
        elif self.status == "elder":
            squeeze = "You respectfully squeeze the elder. It {}.".format(feedback)
        elif self.status == "carcass":
            squeeze = "You regretfully squeeze the carcass."

        self.check_in()

        return squeeze

    def grant_false_name(self, new_false_name):
        """
        Overwrites old false name with new one; activates check-in.
        """

        self.false_name = new_false_name.lower()

        self.name_level = 5
        name_distance = self.name_difference()

        if name_distance == 0:
            self.name_level = 0
        elif name_distance <= 8:
            self.name_level = int(name_distance / 2)

        self.check_in()

        return config.name_level.get(self.name_level)

    def call(self):
        """
        Responds to name being called.
        """

        if self.status == "carcass":
            response = """\
"{}!" you state in a clear voice.

Nothing happens.""".format(self.false_name.capitalize())
        elif self.true_name == self.false_name:
            # for when true name is found
            response = """\
"{}!" you state in a clear voice.

A great electrical warmth crosses the space between you and the {}.

{}
""".format(self.false_name.capitalize(), self.status,
        config.name_level.get(self.name_level))

        else:
            # individual phase feedback to give name hints

            feedback = ""

            if self.status == "larva":
                # squeak for number of letters in true name
                feedback = """\
, opening its mouth to
emit {} squeaks""".format(p.number_to_words(len(self.true_name)))

            elif self.status == "adult":
                # speak blanks for true name
                dots = []
                for x in list(self.true_name):
                    dots.append("[ ]")
                feedback = """\
, making eye contact
with you as it says, "{}"\
""".format("-".join(dots))

            elif self.status == "elder":
                # reveal one letter per age percentage phase
                name_breakdown = list(self.true_name)
                hint_division = int(100/len(name_breakdown))
                name_hint = []
                for x in range(int(self.age_percentage()/hint_division)+1):
                    name_hint.append(name_breakdown.pop(0).capitalize())
                for x in list(name_breakdown):
                    name_hint.append("[ ]")
                feedback = """\
, making eye contact
with you as it says, "{}"\
""".format("-".join(name_hint))

            response = """\
"{false}!" you state in a clear voice. The {status} perks up{feedback}.

{check}
    """.format(false=self.false_name.capitalize(),
            status=self.status,
            check=config.name_level.get(self.name_level),
            feedback=feedback)

        self.check_in()

        return response

    ## formulas

    def interaction_ready(self):
        """
        Calculates if tama is ready for another interaction. This is based on
        temperament and last check-in time.
        """

        diff = time.time() - self.last_checked

        ready_table = {
                "calm": 60*2,
                "cautious": 60*60,
                "clairvoyant": 60,
                "cybre": 1
                }

        multiplier = ready_table.get(self.temperament, 60*10)

        ready = random.randrange(int(.5*multiplier), multiplier)
        #print("timer:")
        #print(ready)
        #print("diff:")
        #print(diff)

        return diff >= ready

    def since_found(self):
        """
        Returns amount of time since tama was found.
        """

        return time.time()-self.found

    def since_phase(self):
        """
        Returns amount of time tama has spent in current phase.
        """

        phase_time = self.phases.get(self.status)

        return time.time()-phase_time

    def name_difference(self):
        """
        Calculates the Levenshtein difference between current true name and
        false name.

        Returns: int
        """

        return Levenshtein.distance(self.true_name, self.false_name)

    def age_percentage(self):
        """
        Calculates the percentage this tama has progressed towards the next
        phase, by subtracting current number of check-ins from the check-ins
        required to complete the current phase.

        Returns: int, representing a percentage 0-100
        """

        if self.status == "carcass":
            # carcasses don't age
            return 0

        last_phase = ""
        if not self.status == "egg":
            phase_number = config.tama_phase.get(self.status)
            last_phase = list(config.tama_phase)[phase_number-1]

        check_ins_this_level = self.check_ins - self.check_in_age.get(last_phase, 0)
        interval = self.check_in_age.get(self.status) - self.check_in_age.get(last_phase, 0)
        percentage = check_ins_this_level / interval * 100

        return percentage

    def age_check(self):
        """
        Checks to see if tama is ready to be aged.

        Returns: bool
        """

        return self.age_percentage() >= 100
